package rexsee.network;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import rexsee.network.SocketAbstractClass.SocketConnectedListener;
import rexsee.network.SocketAbstractClass.SocketDataListener;
import rexsee.network.SocketAbstractClass.SocketFailedListener;
import rexsee.network.SocketAbstractClass.SocketLostListener;
import rexsee.network.SocketAbstractClass.SocketStartedListener;

public class SocketClient extends Thread {

	private final String mClientId;
	private final InetSocketAddress mLocalInetSocketAddress;
	private final InetSocketAddress mRemoteInetSocketAddress;
	private final int mTimeout;
	private final int mReadingCycle;
	private SocketStartedListener mOnStarted = null;
	private SocketConnectedListener mOnConnected = null;
	private SocketFailedListener mOnFailed = null;
	private SocketLostListener mOnLost = null;
	private SocketDataListener mOnRead = null;
	private SocketDataListener mOnWrite = null;
	private Socket mSocket = null;
	private Thread mReadingThread = null;

	public SocketClient(String clientId, InetSocketAddress localInetSocketAddress, InetSocketAddress remoteInetSocketAddress, int timeout, int readingCycle) {
		super();
		mClientId = clientId;
		mLocalInetSocketAddress = localInetSocketAddress;
		mRemoteInetSocketAddress = remoteInetSocketAddress;
		mTimeout = timeout;
		mReadingCycle = readingCycle;
	}

	@Override
	public void run() {
		try {
			mSocket = new Socket();
			mSocket.bind(mLocalInetSocketAddress);
			if (mOnStarted != null) mOnStarted.run(mClientId, false);
			mSocket.connect(mRemoteInetSocketAddress, mTimeout);
			if (mSocket != null && mSocket.isConnected()) {
				mReadingThread = new Thread() {
					@Override
					public void run() {
						try {
							InputStream inputStream = mSocket.getInputStream();
							while (mSocket != null) {
								try {
									int length = inputStream.available();
									if (length == 0) {
										Thread.sleep(mReadingCycle);
									} else {
										byte[] buffer = new byte[length];
										inputStream.read(buffer);
										if (mOnRead != null) mOnRead.run(mClientId, -1, buffer);
									}
								} catch (Exception e) {
									close();
									if (mOnLost != null) mOnLost.run(mClientId, -1, e.getLocalizedMessage());
									break;
								}
							}
						} catch (Exception e) {
						}
					}
				};
				mReadingThread.start();
				if (mOnConnected != null) mOnConnected.run(mClientId, -1);
			} else {
				close();
				if (mOnFailed != null) mOnFailed.run(mClientId, false, "Timeout.");
			}
		} catch (Exception e) {
			close();
			if (mOnFailed != null) mOnFailed.run(mClientId, false, e.getLocalizedMessage());
		}
	}

	public void setListeners(
			SocketStartedListener started,
			SocketFailedListener failed,
			SocketConnectedListener connected,
			SocketLostListener lost,
			SocketDataListener read,
			SocketDataListener write
			) {
		mOnStarted = started;
		mOnFailed = failed;
		mOnConnected = connected;
		mOnLost = lost;
		mOnRead = read;
		mOnWrite = write;
	}

	public boolean isHealthy() {
		return (mSocket == null) ? false : true;
	}
	public boolean isConnected() {
		return mSocket.isConnected();
	}
	public String getClientId() {
		return mClientId;
	}
	public Socket getSocket() {
		return mSocket;
	}
	public InetSocketAddress getLocalInetSocketAddress() {
		return mLocalInetSocketAddress;
	}
	public InetSocketAddress getRemoteInetSocketAddress() {
		return mRemoteInetSocketAddress;
	}
	public int getTimeout() {
		return mTimeout;
	}
	public void close() {
		mReadingThread = null;
		try {
			mSocket.close();
		} catch (Exception e) {
		}
		mSocket = null;
	}

	public boolean write(byte[] bytes) {
		if (mSocket == null) return false;
		try {
			OutputStream outputStream = mSocket.getOutputStream();
			outputStream.write(bytes);
			if (mOnWrite != null) mOnWrite.run(mClientId, -1, bytes);
			return true;
		} catch (Exception e) {
			close();
			if (mOnLost != null) mOnLost.run(mClientId, -1, e.getLocalizedMessage());
			return false;
		}
	}

}
